﻿package extremefx.ui {
	import extremefx.events.KeyEventArgs;	
	import extremefx.tools.ArrayUtils;	
	import extremefx.IDisposable;
	import extremefx.events.EventHandler;
	
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.utils.Dictionary;	

	/**
	 * @author Marcelo Volmaro
	 */
	public final class KeyHandler implements IDisposable {
		private static var _inst:KeyHandler;
		
		private var _keysDown:Dictionary;
		private var _keyDHandlers:Dictionary;
		private var _keyUHandlers:Dictionary;
		
		private var _stage:Stage;
		private var _shift:Boolean;
		private var _alt:Boolean;
		private var _ctrl:Boolean;
		
		public function KeyHandler(pRes:PrivateClass) {
			_stage = UIStage.STAGE;
			_stage.addEventListener(KeyboardEvent.KEY_DOWN, _keyPressed, false, 0, true);
			_stage.addEventListener(KeyboardEvent.KEY_UP, _keyReleased, false, 0, true);
			_stage.addEventListener(Event.DEACTIVATE, _onDeactivate, false, 0, true);
			
			_keysDown = new Dictionary();
			_keyDHandlers = new Dictionary();
			_keyUHandlers = new Dictionary();
		}
		
		public static function get instance():KeyHandler{
			if (_inst == null){
				_inst = new KeyHandler(new PrivateClass());
			}
			
			return _inst;
		}
		
		private var _disposing:Boolean;

		public static function isDown(pKeyCode:uint):Boolean {
			return ((pKeyCode in instance._keysDown) as Boolean);
		}
		
		public static function addKeyHandler(pKeyCode:uint, pDownHandler:Function, pUpHandler:Function = null):void {
			instance._akh(pKeyCode, pDownHandler, pUpHandler);
		}
		
		public static function removeKeyHandler(pKeyCode:uint, pDownHandler:Function, pUpHandler:Function = null):void {
			instance._rkh(pKeyCode, pDownHandler, pUpHandler);
		}
		
		private function _akh(pKeyCode:uint, pDownHandler:Function, pUpHandler:Function):void {
			if (!_keyDHandlers[pKeyCode]) {
				_keyDHandlers[pKeyCode] = [];
			}
			
			if (!_keyUHandlers[pKeyCode]) {
				_keyUHandlers[pKeyCode] = [];
			}
			
			if (pDownHandler != null) ArrayUtils.insertUnique(_keyDHandlers[pKeyCode], pDownHandler);
			if (pUpHandler != null) ArrayUtils.insertUnique(_keyUHandlers[pKeyCode], pUpHandler);
		}
		
		private function _rkh(pKeyCode:uint, pDownHandler:Function, pUpHandler:Function):void {
			if (_keyDHandlers[pKeyCode]) {
				ArrayUtils.remove(_keyDHandlers[pKeyCode], pDownHandler);	
			}
			
			if (_keyUHandlers[pKeyCode]) {
				ArrayUtils.remove(_keyUHandlers[pKeyCode], pUpHandler);	
			}
		}
		
		private function _dispatch(pList:Array):void {
			if (pList && pList.length){
				var specials:uint = (_shift ? 1 : 0) | (_alt ? 2 : 0) | (_ctrl ? 4 : 0);
				for each (var i:Function in pList) {
					i.call(null, specials);
				}
			}
		}

		private function _keyPressed(evt:KeyboardEvent):void {
			var kc:uint = evt.keyCode;
			var alreadyDown:Boolean = _keysDown[kc]; 
			_keysDown[kc] = true;
			
			_shift = evt.shiftKey;
			_alt = evt.altKey;
			_ctrl = evt.ctrlKey;

			if (_eOnKeyDown) _eOnKeyDown.fire(new KeyEventArgs(evt, alreadyDown));
			_dispatch(_keyDHandlers[kc]);
		}
		
		private function _keyReleased(evt:KeyboardEvent):void {
			var kc:uint = evt.keyCode;
			delete _keysDown[kc];
			
			if (_eOnKeyUp) _eOnKeyUp.fire(new KeyEventArgs(evt));
			_dispatch(_keyUHandlers[kc]);
		}
		
		public function get shift():Boolean {
			return _shift;
		}
		
		public function get alt():Boolean {
			return _alt;
		}
		
		public function get ctrl():Boolean {
			return _ctrl;
		}
		
		private function _onDeactivate(e:Event):void {
			_keysDown   = new Dictionary();
		}
		
		public function dispose():void {
			if (_disposing) return;
			_disposing = true;
			
			_stage.removeEventListener(KeyboardEvent.KEY_DOWN, _keyPressed);
			_stage.removeEventListener(KeyboardEvent.KEY_UP, _keyReleased);
			_keysDown = null;
			_stage = null;
			_inst = null;
		}
		
		private static var _eOnKeyUp:EventHandler;
		public static function get onKeyUp():EventHandler{
			if (_eOnKeyUp == null) _eOnKeyUp = new EventHandler(instance);
			return _eOnKeyUp;
		}
		
		private static var _eOnKeyDown:EventHandler;
		public static function get onKeyDown():EventHandler{
			if (_eOnKeyDown == null) _eOnKeyDown = new EventHandler(instance);
			return _eOnKeyDown;
		}
	}
}

final class PrivateClass {}